package org.example;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
// import org.apache.commons.codec.binary.Base64;

import java.util.HashMap;
import java.util.Map;

public class RSAHelper {
    public static final String PRI_KEY_NAME = "RSAPrivateKey";
    public static final String PUB_KEY_NAME = "RSAPublicKey";

    public static final String LF = "\n";
    public static final String CR = "\r";
    public static final String SPACE = " ";
    public static final String EMPTY = "";
    public static final String UTF_8 = "UTF-8";
    /**
     * MD5摘要算法和RSA创建并验证PKCS＃1中定义的RSA数字签名
     * SHA1withRSA
     * SHA224withRSA
     * SHA256withRSA
     * SHA384withRSA
     * SHA512withRSA
     */
    private final static String RSA_SIGNATURE = "MD5withRSA";
    private final static String RSA_CIPHER = "RSA";
    public static final int KEY_SIZE = 1024;
    private static Cipher CIPHER;
    private static KeyFactory RSA_KF;
    static {
        try {
            CIPHER = Cipher.getInstance(RSA_CIPHER);
            RSA_KF = KeyFactory.getInstance(RSA_CIPHER);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            //初始化 失败
            CIPHER = null;
            RSA_KF = null;
        }
    }

    /**
     * 加密
     *
     * @param key
     * @param raw
     * @return
     * @throws Exception
     */
    public static synchronized String encrypt(Key key, String raw) throws Exception {
        // Cipher负责完成加密或解密工作，基于RSA
        Cipher cipher = CIPHER != null ? CIPHER : Cipher.getInstance(RSA_CIPHER);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] data = raw.getBytes(UTF_8);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Integer keySize = getKeySize(key);
        // 对数据分段加密
        cipherSection(cipher, data, out, (keySize / 8) - 11);
        byte[] encryptedData = out.toByteArray();
        out.close();
        return new String(Base64.getEncoder().encode(encryptedData), UTF_8);
    }
    /**
     * 解密
     * @param key
     * @param text
     * @return
     * @throws Exception
     */
    public static synchronized String decrypt(Key key, String text) throws Exception {
        // Cipher负责完成加密或解密工作，基于RSA
        Cipher cipher = CIPHER != null ? CIPHER : Cipher.getInstance(RSA_CIPHER);
        cipher.init(Cipher.DECRYPT_MODE, key);
        //byte[] encryptedData = Base64.getDecoder().decode(text.getBytes(UTF_8));
        byte[] encryptedData = Base64.getDecoder().decode(text.getBytes(UTF_8));

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Integer keySize = getKeySize(key);
        // 对数据分段解密
        cipherSection(cipher, encryptedData, out, keySize / 8);
        byte[] decryptedData = out.toByteArray();
        out.close();
        return new String(decryptedData, UTF_8);
    }
    /**
     * 计算密钥长度
     * @param key 密钥
     * @return
     * */
    private static Integer getKeySize(Key key) {
        BigInteger prime = null;
        if (key instanceof RSAKey) {
            RSAKey a = (RSAKey) key;
            prime = a.getModulus();
        }
        return prime != null ? prime.toString(2).length() : 0;
    }
    /**
     * 密文 分段 处理
     * *
     @param cipher 处理工具
      * @param data 数据源
     * @param out 分段数据流
     * @param decodeLen 分段大小
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    private static void cipherSection(Cipher cipher, byte[] data, ByteArrayOutputStream
        out, int decodeLen)
        throws IllegalBlockSizeException, BadPaddingException {
        int inputLen = data.length;
        int offSet = 0;
        int i = 0;
        byte[] cache;
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > decodeLen) {
                cache = cipher.doFinal(data, offSet, decodeLen);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * decodeLen;
        }
    }
    public static RSAPublicKey getRSAPublicKey(byte[] keyBytes) throws
        NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = RSA_KF != null ? RSA_KF : KeyFactory.getInstance(RSA_CIPHER);
        return (RSAPublicKey) kf.generatePublic(spec);
    }
    /**
     * 获取公钥
     * @param filename
     * @return
     * @throws Exception
     * */
    public static RSAPublicKey getRSAPublicKey(String filename)
        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int) f.length()];
        dis.readFully(keyBytes);
        dis.close();
        return getRSAPublicKey(keyBytes);
    }
    public static RSAPublicKey getPemRSAPublicKey(String contentBase64) throws Exception {
        String publicKeyPEM = contentBase64.replace("-----BEGIN PUBLIC KEY-----",
            EMPTY)
            .replace("-----END PUBLIC KEY-----", EMPTY)
            .replace(LF, EMPTY).replace(CR, EMPTY)
            .replace(SPACE, "+");
        byte[] decoded = Base64.getDecoder().decode(publicKeyPEM.getBytes(UTF_8));
        return getRSAPublicKey(decoded);
    }
    /**
     * 获取私钥
     * @param keyBytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static RSAPrivateKey getRSAPrivateKey(byte[] keyBytes) throws
        NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = RSA_KF != null ? RSA_KF : KeyFactory.getInstance(RSA_CIPHER);
        return (RSAPrivateKey) kf.generatePrivate(spec);
    }

    public static RSAPrivateKey getPemRSAPrivateKey(String contentBase64) throws Exception {
        String privKeyPEM = contentBase64.replace("-----BEGIN PRIVATE KEY-----", EMPTY)
            .replace("-----END PRIVATE KEY-----", EMPTY)
            .replace(LF, EMPTY).replace(CR, EMPTY)
            .replace(SPACE, "+");
        byte[] decoded = Base64.getDecoder().decode(privKeyPEM.getBytes(UTF_8));
        return getRSAPrivateKey(decoded);
    }
    /**
     * 修复RSA http get请求后的被转换 的字符
     * @param rsaContent
     * @return
     * */
    public static String fixRsaContent(String rsaContent){
        if(rsaContent == null){
            return rsaContent;
        }
        String fixRsaContent = rsaContent.replace(LF, EMPTY).replace(CR,
            EMPTY).replace(SPACE, "+");
        return fixRsaContent;
    }

    public static RSAPrivateKey getRSAPrivateKey(String filename)
        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        File f = new File(filename);
        DataInputStream dis = new DataInputStream(new FileInputStream(f));
        byte[] keyBytes = new byte[(int) f.length()];
        dis.readFully(keyBytes);
        dis.close();
        return getRSAPrivateKey(keyBytes);
    }
    /**
     * 生成rsa公钥和密钥 byte文件
     * @param RSAPublicKeyFilename
     * @param RSAPrivateKeyFilename
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * */
    public static Map<String, byte[]> generateByteKey(String RSAPublicKeyFilename,
                                                      String RSAPrivateKeyFilename)
        throws Exception {
        Map<String, byte[]> keyPair = generateRSAKey(KEY_SIZE);
        byte[] RSAPublicKeyBytes = keyPair.get(PUB_KEY_NAME);
        FileOutputStream fos = new FileOutputStream(RSAPublicKeyFilename);
        fos.write(RSAPublicKeyBytes);
        fos.close();
        byte[] RSAPrivateKeyBytes = keyPair.get(PRI_KEY_NAME);
        fos = new FileOutputStream(RSAPrivateKeyFilename);
        fos.write(RSAPrivateKeyBytes);
        fos.close();
        HashMap<String, byte[]> keyMap = new HashMap<>(2);
        keyMap.put(PUB_KEY_NAME, RSAPublicKeyBytes);
        keyMap.put(PRI_KEY_NAME, RSAPrivateKeyBytes);
        return keyMap;
    }

    /**
     * 生成rsa公钥和密钥 base64 encode 文件
     * @param RSAPublicKeyFilename
     * @param RSAPrivateKeyFilename
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @deprecated 对rsa进行base64编码后写入文件，文件夹不存在抛异常。
     */
    @Deprecated
    public static Map<String, String> generateBase64Key(String RSAPublicKeyFilename,
                                                        String RSAPrivateKeyFilename)
        throws Exception {
        Map<String, byte[]> keyPair = generateRSAKey(KEY_SIZE);
        String stringPubKey = new
            String(Base64.getEncoder().encode(keyPair.get(PUB_KEY_NAME)), UTF_8);
        OutputStreamWriter osw = new OutputStreamWriter(new
            FileOutputStream(RSAPublicKeyFilename));
        osw.write(stringPubKey);
        osw.close();
        String stringPriKey = new
            String(Base64.getEncoder().encode(keyPair.get(PRI_KEY_NAME)), UTF_8);
        osw = new OutputStreamWriter(new FileOutputStream(RSAPrivateKeyFilename));
        osw.write(stringPriKey);
        osw.close();
        Map<String, String> keymap = new HashMap<>(2);
        keymap.put(PRI_KEY_NAME, stringPriKey);
        keymap.put(PUB_KEY_NAME, stringPubKey);
        return keymap;
    }

    /**
     * 生成rsa公钥和密钥
     * @param keySize 密钥长度
     * @return
     * * @throws NoSuchAlgorithmException
     * */
    public static Map<String, byte[]> generateRSAKey(Integer keySize) throws Exception
    {
        if(!is2OfPower(keySize)) {
            throw new Exception("keySize Invalid, Must be greater than 512 and be a power of 2!");
        }

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_CIPHER);
        SecureRandom secureRandom = new SecureRandom();
        keyPairGenerator.initialize(keySize, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        Map<String, byte[]> keymap = new HashMap<>(2);
        keymap.put(PRI_KEY_NAME, keyPair.getPrivate().getEncoded());
        keymap.put(PUB_KEY_NAME, keyPair.getPublic().getEncoded());
        return keymap;
    }
    /**
     * 判断必须大于512并且是2的整数幂。
     * @param number
     * @return
     * */
    public static boolean is2OfPower(int number) {
        if (number < 512) {
            return false;
        } else {
            String temp = Integer.toBinaryString(number);
            return temp.lastIndexOf('1') == 0;
        }
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data 元数据
     * @param RSAPrivateKey 私钥
     * @return sign Base64 encode
     * @throws Exception
     * */
    public static synchronized String sign(String data, RSAPrivateKey RSAPrivateKey)
        throws Exception {
        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(RSA_SIGNATURE);
        signature.initSign(RSAPrivateKey);
        signature.update(data.getBytes(UTF_8));
        return new String(Base64.getEncoder().encode(signature.sign()), UTF_8);
    }

    /**
     * 校验数字签名
     * @param data 加密数据
     * @param RSAPublicKey 公钥
     * @param sign 数字签名
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     * */
    public static synchronized boolean verify(String data, RSAPublicKey RSAPublicKey, String sign) throws Exception {
        sign = sign.replace(SPACE, "+");
        Signature signature = Signature.getInstance(RSA_SIGNATURE);
        signature.initVerify(RSAPublicKey);
        signature.update(data.getBytes(UTF_8));
        byte[] decode = Base64.getDecoder().decode(sign.getBytes(UTF_8));
        // 验证签名是否正常
        return signature.verify(decode);
    }

    public static void main1(String[] args) {
        String orgPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwT9obzkKrzNKC6p+zYdoQ85fXRn8dkNiOSwAq9LFASsPVZimFqKa650nT6FOHvXpqwcJuzQDTA/2B4FslKX68Vw5EY2ih/ZoTsbSKeCsEW9/M8gDGUtblpNSyuonFHdHQbHV2ko4O7foz5fHFSwBNCnKiMQGkKbYC+MWi35rwFQIDAQAB";
        String orgPrivateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPBP2hvOQqvM0oLqn7Nh2hDzl9dGfx2Q2I5LACr0sUBKw9VmKYWoprrnSdPoU4e9emrBwm7NANMD/YHgWyUpfrxXDkRjaKH9mhOxtIp4KwRb38zyAMZS1uWk1LK6icUd0dBsdXaSjg7t+jPl8cVLAE0KcqIxAaQptgL4xaLfmvAVAgMBAAECgYAM0XmVG5yvTMrazFiN6TWIS3IJO9uqcFA40NPWtrwgrYxCHQOwezU/iXhE6yLPfgIiYSj1n45AczlJvPWnpw25NCZIKt1Z8QkeiIOGeinsfLsPIFCBkk1R7riFVRdQkNMJufZkcAlcV3cI7lZOGf7HZpLyA1WxniPRie8c8fn2KQJBAPq6TXXg5gst9+C/kU5ddttJa2agtBgSWgfxUZ/Bzi88ClTEqAgi5jXKuxCTH2MjNv/kiOMcaJk7YYd3O8yrYS0CQQD1XXrTa/T3V7oUJI5qBCLPgnRDsKJv3mW2YFO/+zJw4AbwcrH8SlwyJMmsJCie6kszMRHeefhLeyYCn6TR8wuJAkA+m7jt/4vSLNdh8tQo+mPMnEfKZ8Sw4hxDMXpNZ0rCNr7prfW7mmNb+VS+7c3/pnJ8cafQ9m4UHWTcO+Td1cm9AkEAtlBXS3ET/BU7NmxTRYL9OqL8MK+2gtkFSpYYpLXJrQVJIrCWf5fhGYEsOLRFWo9MgSUc2epXC/7dJ2Xn2fh1wQJBAOnhLopAiAN1HL7R7Axm67AFHorrSoLSwdkrIuA71vaVMVt9n2ZJCK7z19H/FY6QHnr2TGRAaGnJn5+/TBSu9og=";
        String sipPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHPTxKy8y9MOGI2h52DuVvk0RlOv2NjBTY88frHLk3qRYU24ymdGV4H++ta8DZiMJNa1OI1oAyYMVlC8OZaENWTnUPJ2Et76jnQ1zdtnm1fl75l863nYyJx+qJwohBTqIHGtQpLp6pZHLWLYg6B7UYkpna7TOTjEpKmVop6qO2cQIDAQAB";
        String sipPrivateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIc9PErLzL0w4YjaHnYO5W+TRGU6/Y2MFNjzx+scuTepFhTbjKZ0ZXgf761rwNmIwk1rU4jWgDJgxWULw5loQ1ZOdQ8nYS3vqOdDXN22ebV+XvmXzredjInH6onCiEFOogca1CkunqlkctYtiDoHtRiSmdrtM5OMSkqZWinqo7ZxAgMBAAECgYAJS1QRb+AqQGm/lf3x7yQlfuF8c4lpaO8l2dsrdVXlVWGHHW+VIPq4PrmiGX2vdY6k83NSPCujSrNGNoDC0j+OXM6hCmE3FBzws7K0Bt7PnS3f/1l2Rsx8aGDq+XpJEhUICx/v5GfXeqmTgm/0HmgEcZO/QfRI3LpRkEsMkx8OgQJBAONjWcgvyP2uWLmvZlfawWgLbusG0buxXH+AOFHwDg3TtWPi7boRS6IRzOFO/iya6+UZc3UVQMkdzcC8SHjZRGUCQQCYQZM2JB/7TJXG3pccEokBGQuDxjbOJZEKuifs4qCW2nm4Trr7KRwjoFgwSiPZgMa7li7C8ZoYBe9IqspbwisdAkBG6XbYXT6bDkIRNkf+YfQq1FX06Z2CoR8ti/kEZI6ddUZ+LgjhG/+wPUjdgtr1YiLqwXMmMkiP78F4t78KIQeZAkEAjXvCNHl1TqxoMhj+CpQew+pmDNnQa9f05CcAmtwtpoD2wxJGJsaY43JJAPakQaWtBvwUEAs6ykAZj0lGKeZQ8QJABb7ocsVGdLlPCe/2oKdUuWCDS4U72DmLP1xz20nFzfYAuuch4WTz+8cYW8Gb0HvWLBkij8ov3aLiYXZnddBFPA==";
        try {
            //原始数据
            String origData ="{ 'userName': 'Thomas', + 'userId': '123'}";
            //供应商公私钥
            RSAPublicKey orgPubKeyObj = RSAHelper.getPemRSAPublicKey(orgPubKey);
            RSAPrivateKey orgPriKeyObj = RSAHelper.getPemRSAPrivateKey(orgPrivateKey);
            //sip公私钥
            RSAPublicKey sipPubKeyObj = RSAHelper.getPemRSAPublicKey(sipPubKey);
            RSAPrivateKey sipPriKeyObj = RSAHelper.getPemRSAPrivateKey(sipPrivateKey);
            //sip公钥对数据加密，供应商私钥对数据加签
            String data = encrypt(sipPubKeyObj,origData);
            String signData = sign(origData,orgPriKeyObj);
            System.out.println("data = " + data);
            System.out.println("signData = " + signData);
            //sip私钥解密，供应商公钥验签
            String decData = decrypt(sipPriKeyObj,data);
            System.out.println(decData);
            System.out.println("verify = " + verify(decData,orgPubKeyObj,signData));} catch (Exception e) {
            e.printStackTrace();
        }
    }
}
